﻿using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Moon.Sql
{
    internal class DynamicRow : IDynamicMetaObjectProvider
    {
        private Dictionary<string, object> _values = null;

        public DynamicRow(Dictionary<string, object> values)
        {
            this._values = values ?? throw new ArgumentNullException("parameter error");
        }

        public DynamicMetaObject GetMetaObject(Expression parameter)
        {
            return new DynamicRowMetaObject(parameter, this);
        }

        public object SetDictionaryEntry(string key, object value)
        {
            if (_values.ContainsKey(key))
                _values[key] = value;
            else
                _values.Add(key, value);
            return value;

        }

        public object GetDictionaryEntry(string key)
        {
            object result = null;
            if (_values.ContainsKey(key))
            {
                result = _values[key];
            }
            return result;
        }
    }

    internal class DynamicRowMetaObject : DynamicMetaObject
    {
        private static readonly MethodInfo getValueMethod = typeof(DynamicRow).GetMethod("GetDictionaryEntry");
        private static readonly MethodInfo setValueMethod = typeof(DynamicRow).GetMethod("SetDictionaryEntry", new Type[] { typeof(string), typeof(object) });

        internal DynamicRowMetaObject(Expression parameter, DynamicRow dynamicDictionary)
            : base(parameter, BindingRestrictions.Empty, dynamicDictionary)
        {
        }

        private System.Dynamic.DynamicMetaObject CallMethod(
                MethodInfo method,
                System.Linq.Expressions.Expression[] parameters)
        {
            var callMethod = new System.Dynamic.DynamicMetaObject(
                System.Linq.Expressions.Expression.Call(
                    System.Linq.Expressions.Expression.Convert(Expression, LimitType),
                    method,
                    parameters),
                System.Dynamic.BindingRestrictions.GetTypeRestriction(Expression, LimitType)
                );
            return callMethod;
        }

        public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
        {
            var parameters = new System.Linq.Expressions.Expression[]
                                   {
                                         System.Linq.Expressions.Expression.Constant(binder.Name),
                                         value.Expression,
                                   };

            var callMethod = CallMethod(setValueMethod, parameters);
            return callMethod;
        }

        public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
        {
            var parameters = new System.Linq.Expressions.Expression[]
                                   {
                                         System.Linq.Expressions.Expression.Constant(binder.Name)
                                   };

            var callMethod = CallMethod(getValueMethod, parameters);
            return callMethod;
        }
    }
}
